/* hexedit -- Hexadecimal Editor for Binary Files
   Copyright (C) 1998 Pixel (Pascal Rigaux)

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.*/

#include <ctype.h>

#include "dhexview.h"

static int searchA(char **string, int *sizea, char *tmp, int tmp_size,
                   bool use_last);
static void searchB(INT loc, char *string);

/*
 * Remove spaces from a string in-line.
 */
static void
despace_string (char *s, int *len)
{
    char *d;
    for (d = s; *s; ++s) {
        if (isspace(*s)) {
            --*len;
            continue;
        }
        *d++ = *s;
    }
    return;
}

/*******************************************************************************/
/* Search functions */
/*******************************************************************************/
static int searchA(char **string, int *sizea, char *tmp, int tmp_size,
                   bool use_last)
{
    char *msg =
        hexOrAscii ? "Hexa string to search: " :
        "Ascii string to search: ";
    char **last = hexOrAscii ? &lastAskHexString : &lastAskAsciiString;

    if (use_last && *last) {
        int len = strlen(*last);
        displayTwoLineMessage(*last, "ok");
        memcpy(tmp, *last, len + 1);
    } else {
        if (!ask_about_save_and_redisplay())
            return false;
        if (!displayMessageAndGetString(msg, last, tmp, tmp_size))
            return false;
    }

    *sizea = strlen(tmp);
    if (hexOrAscii) {
        despace_string(tmp, sizea);
        if (!hexStringToBinString(tmp, sizea))
            return false;
    }

    *string = malloc(*sizea);
    memcpy(*string, tmp, *sizea);

    nodelay(stdscr, true);
    displayTwoLineMessage("searching...", "(press any key to cancel)");
    return true;
}

static void searchB(INT loc, char *string)
{
    nodelay(stdscr, false);
    free(string);

    if (loc >= 0)
        set_cursor(loc);
    else {
        if (loc == -3)
            displayMessageAndWaitForKey("not found");
    }
}

void search_forward (bool use_last)
{
    char *p, *string, tmp[BLOCK_SEARCH_SIZE], tmpstr[BLOCK_SEARCH_SIZE];
    int quit, sizea, sizeb;
    INT blockstart;

    if (!searchA(&string, &sizea, tmp, sizeof(tmp), use_last))
        return;

    if (!use_last) {
        crule_t *rule = crule_new(CRULE_TYPE_EXACT);
        rule->set_exact_string(rule, (unsigned char *) string, sizea);
        rule->add_color(rule, 0, 0, COLOR_BLACK, COLOR_YELLOW);

        hlsearch_crulist->destroy_rules(hlsearch_crulist);
        hlsearch_crulist->add(hlsearch_crulist, rule);
        hlsearch_crulist->init_colors(hlsearch_crulist, &color_pairs);
    }

    quit = -1;
    blockstart = base + cursor - BLOCK_SEARCH_SIZE + sizea;
    do {
        blockstart += BLOCK_SEARCH_SIZE - sizea + 1;
        if (LSEEK_(fd, blockstart) == -1) {
            quit = -3;
            break;
        }
        if ((sizeb = read(fd, tmp, BLOCK_SEARCH_SIZE)) < sizea)
            quit = -3;
        else if (getch() != ERR)
            quit = -2;
        else if ((p = mymemmem(tmp, sizeb, string, sizea)))
            quit = p - tmp;

        sprintf(tmpstr, "searching... 0x%08llX", (long long) blockstart);
        nodelay(stdscr, true);
        displayTwoLineMessage(tmpstr, "(press any key to cancel)");

    } while (quit == -1);

    searchB(quit + (quit >= 0 ? blockstart : 0), string);
}

void search_backward (bool use_last)
{
    char *p, *string, tmp[BLOCK_SEARCH_SIZE], tmpstr[BLOCK_SEARCH_SIZE];
    int quit, sizea, sizeb;
    INT blockstart;

    if (!searchA(&string, &sizea, tmp, sizeof(tmp), use_last))
        return;

    if (!use_last) {
        crule_t *rule = crule_new(CRULE_TYPE_EXACT);
        rule->set_exact_string(rule, (unsigned char *) string, sizea);
        rule->add_color(rule, 0, 0, COLOR_BLACK, COLOR_YELLOW);

        hlsearch_crulist->destroy_rules(hlsearch_crulist);
        hlsearch_crulist->add(hlsearch_crulist, rule);
        hlsearch_crulist->init_colors(hlsearch_crulist, &color_pairs);
    }

    quit = -1;
    blockstart = base + cursor - sizea + 1;
    do {
        blockstart -= BLOCK_SEARCH_SIZE - sizea + 1;
        sizeb = BLOCK_SEARCH_SIZE;
        if (blockstart < 0) {
            sizeb -= -blockstart;
            blockstart = 0;
        }

        if (sizeb < sizea)
            quit = -3;
        else {
            if (LSEEK_(fd, blockstart) == -1) {
                quit = -3;
                break;
            }
            if (sizeb != read(fd, tmp, sizeb))
                quit = -3;
            else if (getch() != ERR)
                quit = -2;
            else if ((p = mymemrmem(tmp, sizeb, string, sizea)))
                quit = p - tmp;
        }

        sprintf(tmpstr, "searching... 0x%08llX", (long long) blockstart);
        nodelay(stdscr, true);
        displayTwoLineMessage(tmpstr, "(press any key to cancel)");

    } while (quit == -1);

    searchB(quit + (quit >= 0 ? blockstart  - sizea  + 1 : 0), string);
}
